home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 424_01 / ed_157 / init_term.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-09  |  31.2 KB  |  1,176 lines

  1. /*
  2.  * Copyright (C) 1992 by Rush Record
  3.  * Copyright (C) 1993 by Charles Sandmann (sandmann@clio.rice.edu)
  4.  * 
  5.  * This file is part of ED.
  6.  * 
  7.  * ED is free software; you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation.
  9.  * 
  10.  * ED is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  11.  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  12.  * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  13.  * 
  14.  * You should have received a copy of the GNU General Public License along with ED
  15.  * (see the file COPYING).  If not, write to the Free Software Foundation, 675
  16.  * Mass Ave, Cambridge, MA 02139, USA.
  17.  */
  18. #include "opsys.h"
  19.  
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23.  
  24. #define MAXSTRING 64    /* the longest string of chars sent by a single keystroke */
  25.  
  26. #include "memory.h"
  27. #include "file.h"
  28. #include "ctyp_dec.h"
  29. #include "rec.h"
  30. #include "window.h"
  31. #include "ed_dec.h"
  32. #include "emul_dec.h"
  33. #include "handy.h"
  34. #include "term_cmd.h"
  35. #include "keyvals.h"
  36.  
  37. typedef struct tree_str *tree_ptr;
  38. typedef struct tree_str {Int branches,leaf,value;Schar *chars;tree_ptr *nodes;} tree_node;
  39.  
  40. static Char **coms;
  41. static tree_node base = {0,0,0,NULL,NULL};
  42. static Schar history[MAXSTRING];    /* the chars in the (still-being-analyzed) keystring so far */
  43. static Schar *next_history;    /* where the next history char goes */
  44. static Schar *report_history;    /* pointer to next history char to be returned */
  45. static tree_ptr cur;    /* where we are in the tree */
  46. static Schar chrbuf = 0;    /* to receive one char from keyboard */
  47. static Int evolving = 0;    /* whether we are still getting chars from a defined key */
  48. static Int scroll_top,scroll_bot;    /* the last set scrolling region boundaries */
  49. static Int recovering = 0;    /* whether we are recovering from journal */
  50. static Int processmode = 0;    /* whether we are in silent, 'process' mode */
  51. static Int vtstartyet = 0;
  52. static Schar *emulbuf = NULL;    /* buffer to hold emulation characters */
  53. static Schar *emulptr;    /* points to next emulated key */
  54. static Schar *emulend;    /* points to stopping point */
  55. static Int *reptbuf;    /* buffer to hold repeat counts */
  56. static Int *reptptr;    /* points to next repeat count */
  57. static Int *reptend;    /* points to stopping point */
  58. static Int emulsiz = 0;    /* current size of emulation buffer */
  59.  
  60. extern void up();
  61. extern void down();
  62. extern void vtstart();
  63. extern Char *exefile();
  64. extern Char *filename();
  65.  
  66. /******************************************************************************\
  67. |Routine: emulate_abort
  68. |Callby: abort_key
  69. |Purpose: To stop emulation if a stop-the-defined-key situation arises.
  70. |Arguments:
  71. |    none
  72. \******************************************************************************/
  73. void emulate_abort()
  74. {
  75.     if(emulptr != emulend)
  76.     {
  77.         emulptr = emulend;
  78.         reptptr = reptend;
  79.         puton();
  80.         ref_display();
  81.     }
  82. }
  83.  
  84. /******************************************************************************\
  85. |Routine: emulate_key
  86. |Callby: autowrp edit
  87. |Purpose: To make it as though the user typed some keys.
  88. |Arguments:
  89. |    val is the value of the key we pretend the user pressed.
  90. |    repeat is the repeat count for the key.
  91. \******************************************************************************/
  92. void emulate_key(val,repeat)
  93. Schar val;
  94. Int repeat;
  95. {
  96.     Schar *temp,*to,*from;
  97.     Int *ltemp,*lto,*lfrom;
  98.     
  99.     if(repeat != 0)
  100.     {
  101.         if(emulend >= emulbuf + emulsiz)
  102.         {
  103. /* if there is space at beginning of array, slide everything down */
  104.             if(emulptr > emulbuf)
  105.             {
  106.                 for(from = emulptr,to = emulbuf;from != emulend;*to++ = *from++);
  107.                 emulptr  = emulbuf;
  108.                 emulend = to;
  109.                 for(lfrom = reptptr,lto = reptbuf;lfrom != reptend;*lto++ = *lfrom++);
  110.                 reptptr  = reptbuf;
  111.                 reptend = lto;
  112.             }
  113.             else    /* generate new buffers and copy */
  114.             {
  115. /* generate new char buffer */
  116.                 temp = (Schar *)imalloc(emulsiz << 1);
  117.                 memcpy(temp,emulbuf,emulsiz);
  118.                 emulend = temp + (emulend - emulbuf);
  119.                 emulptr = temp + (emulptr - emulbuf);
  120.                 ifree(emulbuf);
  121.                 emulbuf = temp;
  122. /* generate new repeat buffer */
  123.                 ltemp = (Int *)imalloc((emulsiz << 1) * sizeof(Int));
  124.                 memcpy(ltemp,reptbuf,emulsiz * sizeof(Int));
  125.                 reptend = ltemp + (reptend - reptbuf);
  126.                 reptptr = ltemp + (reptptr - reptbuf);
  127.                 ifree(reptbuf);
  128.                 reptbuf = ltemp;
  129. /* increase size */
  130.                 emulsiz <<= 1;
  131.             }
  132.         }
  133.         *emulend++ = val;
  134.         *reptend++ = repeat;
  135. /*        if(repeat > 19)    11-93 CWS bug, no way to puton()!
  136.             putoff(); */
  137.     }
  138. }
  139.  
  140. /******************************************************************************\
  141. |Routine: set_processmode
  142. |Callby: main
  143. |Purpose: Determines whether display is turned back on after multiply-repeated
  144. |         defined keys.
  145. |Arguments:
  146. |    mode is the 'process' mode flag.
  147. \******************************************************************************/
  148. void set_processmode(mode)
  149. Int mode;
  150. {
  151.     processmode = mode;
  152. }
  153.  
  154. /******************************************************************************\
  155. |Routine: trans_term_string
  156. |Callby: init_term restore_par
  157. |Purpose: Handles the control-character quoting convention used in the
  158. |         terminal configuration files. Control characters that might interfere
  159. |         with reading the file are quoted with the ascii character 0x1d.
  160. |Arguments:
  161. |    buf is the buffer holding the string from the configuration file.
  162. |    l is the returned (perhaps modified) length of the processed string.
  163. \******************************************************************************/
  164. void trans_term_string(buf,l)
  165. Char *buf;
  166. Int *l;
  167. {
  168.     register Int k,v;
  169.     register Char *c,*start,*end;
  170.  
  171.     for(start = c = buf,end = buf + *l;buf != end;)
  172.     {
  173.         if((v = *buf++) == 0x1d)
  174.             for(k = v = 0;k < 3;k++)
  175.                 v = 10 * v + *buf++ - '0';
  176.         *c++ = v;
  177.     }
  178.     *c = 0;
  179.     *l = c - start;
  180. }
  181.  
  182. /******************************************************************************\
  183. |Routine: init_terminal
  184. |Callby: main
  185. |Purpose: Initializes the terminal for editing. Processes the user-specified
  186. |         terminal configuration file, and sets up the screen database.
  187. |Arguments:
  188. |    term is the name of the terminal configuration file.
  189. |    setup is the name of the user's setup file.
  190. |    cnrow,cncol are buffers containing the number of lines and columns on
  191. |            the terminal screen.
  192. \******************************************************************************/
  193. void init_terminal(term,setup,cnrow,cncol)
  194. Char *term,*setup,*cnrow,*cncol;
  195. {
  196.     FILE *fp,*ifp,*ofp;
  197.     Int l,i,j,b,nkeys;
  198.     tree_ptr t,*new,newnode;
  199.     Char buf[512],defkeyfile[512];
  200.     Schar *newchars;
  201.     struct stat statbuf;
  202.  
  203.     if(my_sscanf(cnrow,"%d",&NROW) != 1)
  204.     {
  205.         printf("Number of screen lines is illegible (specified as %s).\r\n",cnrow);
  206.         cleanup(-1);
  207.     }
  208.     if(my_sscanf(cncol,"%d",&NCOL) != 1)
  209.     {
  210.         printf("Number of screen columns is illegible (specified as %s).\r\n",cncol);
  211.         cleanup(-1);
  212.     }
  213.     if(!(fp = fopen(term,"r")))
  214.     {
  215.         printf("Can't open configuration file %s.\r\n",term);
  216.         cleanup(-1);
  217.     }
  218.     BRAINDEAD = 1;
  219.     coms = (Char **)imalloc((int)NCOMS * sizeof(Char *));
  220.     for(i = 0;i < (int)NCOMS;i++)
  221.     {
  222.         if(!fgets(buf,sizeof(buf),fp))
  223.         {
  224.             printf("Error reading configuration file.\r\n");
  225.             cleanup(-1);
  226.         }
  227.         l = strlen(buf) - 1;
  228.         buf[l] = '\0';
  229.         trans_term_string(buf,&l);
  230.         coms[i] = (Char *)imalloc(l + 1);
  231.         strcpy(coms[i],buf);
  232.         if(i != (int)EOB && coms[i][0] != '~')
  233.             BRAINDEAD = 0;
  234.     }
  235.     my_fscanf(fp,"%d\n",&nkeys);
  236.     for(i = 1;i <= nkeys;i++)
  237.     {
  238.         if(!fgets(buf,sizeof(buf),fp))
  239.         {
  240.             printf("Error reading key strings from configuration file.\r\n");
  241.             cleanup(-1);
  242.         }
  243.         buf[(l = strlen(buf) - 1)] = '\0';
  244.         trans_term_string(buf,&l);
  245.         t = &base;
  246.         for(j = 0;j < l;j++)
  247.         {
  248.             for(b = 0;b < t->branches;b++)
  249.                 if(t->chars[b] == buf[j])
  250.                     break;
  251.             if(b < t->branches)    /* match for char at current node, just advance to child node */
  252.                 t = t->nodes[b];
  253.             else    /* no match for this char at this node */
  254.             {
  255. /* allocate a bigger array of tree pointers, transfer the old if any, and add the new pointer at the end */
  256.                 new = (tree_ptr *)imalloc((t->branches + 1)*sizeof(tree_ptr));
  257.                 if(t->branches > 0)
  258.                 {
  259.                     memcpy(new,t->nodes,t->branches * sizeof(tree_ptr));
  260.                     ifree(t->nodes);
  261.                 }
  262.                 t->nodes = new;
  263.                 newnode = new[t->branches] = (tree_ptr)imalloc(sizeof(tree_node));
  264. /* set the new node up as empty */
  265.                 newnode->branches = newnode->leaf = 0;
  266. /* allocate a bigger array for characters, transfer the old, add the new char at the end */
  267.                 newchars = (Schar *)imalloc(t->branches + 1);
  268.                 if(t->branches > 0)
  269.                 {
  270.                     memcpy(newchars,t->chars,t->branches);
  271.                     ifree(t->chars);
  272.                 }
  273.                 t->chars = newchars;
  274.                 newchars[t->branches] = buf[j];
  275. /* one more char at this node now */
  276.                 t->branches++;
  277. /* we now sit at new node */
  278.                 t = newnode;
  279.             }
  280.         }    /* get next char in string */
  281. /* flag the leaf (pointed to by t) as a termination */
  282.         t->leaf = 1;
  283.         t->value = -i;
  284.     }    /* get next key string */
  285.     fclose(fp);
  286.     cur = &base;    /* initialize tree search */
  287.     next_history = history;    /* point to start of history buffer */
  288.  
  289.     emulbuf = emulptr = emulend = (Schar *)imalloc(emulsiz = 512);
  290.     reptbuf = reptptr = reptend = (Int *)imalloc(emulsiz * sizeof(Int));
  291.  
  292.     if(!NROW)
  293.     {
  294.         strcpy(buf,"$ROWS");
  295.         envir_subs(buf);
  296.         if(strcmp(buf,"$ROWS"))
  297.         {
  298.             if(my_sscanf(buf,"%d",&i) == 1)
  299.                 NROW = i;
  300.         }
  301.     }
  302.     if(!NCOL)
  303.     {
  304.         strcpy(buf,"$COLS");
  305.         envir_subs(buf);
  306.         if(strcmp(buf,"$COLS"))
  307.         {
  308.             if(my_sscanf(buf,"%d",&i) == 1)
  309.                 NCOL = i;
  310.         }
  311.     }
  312.     vtstart();
  313.     ttysetsize(&NROW,&NCOL);
  314.     scroll_top = 1;
  315.     scroll_bot = NROW;
  316.     EMU_NROW = NROW;    /* set up emulation layer */
  317.     EMU_NCOL = NCOL;
  318.     EMU_SCREEN = (Char *)imalloc(EMU_NROW*EMU_NCOL);
  319.     EMU_ATTRIB = (Char *)imalloc(EMU_NROW*EMU_NCOL);
  320.     memset(EMU_SCREEN,' ',EMU_NCOL * EMU_NROW);
  321.     memset(EMU_ATTRIB,0,EMU_NCOL * EMU_NROW);
  322.     EMU_CURROW = -1;    /* flag that it is not defined for put */
  323. /* do the user-specific setup file */
  324.     strcpy(KEYFILE,setup);
  325.     strip_quotes(KEYFILE);
  326.     envir_subs(KEYFILE);
  327.     if(stat(KEYFILE,&statbuf))
  328.     {
  329. #ifdef VMS
  330.         if((ofp = fopen(KEYFILE,"w","ctx=rec","rfm=var","rat=cr","shr=nil")))
  331. #else
  332.         if((ofp = fopen(KEYFILE,"w")))
  333. #endif
  334.         {
  335.             strcpy(defkeyfile,exefile());
  336.             strcpy(filename(defkeyfile),"ed.setup");
  337.             if((ifp = fopen(defkeyfile,"r")))
  338.             {
  339.                 while(fgets(buf,sizeof(buf),ifp))
  340.                     fputs(buf,ofp);
  341.                 fclose(ifp);
  342.             }
  343.             fclose(ofp);
  344.         }
  345.     }
  346. /* these have to be set to make wait_message work right (restore_par() may call slip_message/wait_message) */
  347.     BOTROW = NROW;
  348.     FIRSTCOL = 1;
  349.     restore_par(-1);    /* the -1 means restore all params, not just one in particular */
  350. }
  351.  
  352. /******************************************************************************\
  353. |Routine: command_one
  354. |Callby: init_term
  355. |Purpose: Issues a terminal command that has one integer parameter.
  356. |Arguments:
  357. |    command is the terminal command buffer.
  358. |    parm is the single parameter.
  359. \******************************************************************************/
  360. void command_one(command,parm)
  361. Char *command;
  362. Int parm;
  363. {
  364.     register Int i;
  365.     register Char *p,*q,c;
  366.     Char buf[128];
  367.  
  368.     if(puttest())
  369.     {
  370.         for(p = command,q = buf;(c = *p++);)
  371.         {
  372.             if(c == '%')
  373.             {
  374.                 p++;
  375.                 if(parm > 99)
  376.                 {
  377.                     *q++ = (i = (parm / 100)) + '0';
  378.                     parm -= 100 * i;
  379.                     *q++ = (i = (parm / 10)) + '0';
  380.                     parm -= 10 * i;
  381.                     *q++ = parm + '0';
  382.                 }
  383.                 else if(parm > 9)
  384.                 {
  385.                     *q++ = (i = (parm / 10)) + '0';
  386.                     parm -= 10 * i;
  387.                     *q++ = parm + '0';
  388.                 }
  389.                 else
  390.                     *q++ = parm + '0';
  391.             }
  392.             else
  393.                 *q++ = c;
  394.         }
  395.         *q = '\0';
  396.         put(buf);
  397.     }
  398. }
  399.  
  400. /******************************************************************************\
  401. |Routine: command_two
  402. |Callby: init_term
  403. |Purpose: Issues a terminal command that has two integer parameters.
  404. |Arguments:
  405. |    command is the terminal command buffer.
  406. |    parm1,parm2 are the parameters.
  407. \******************************************************************************/
  408. void command_two(command,parm1,parm2)
  409. Char *command;
  410. Int parm1,parm2;
  411. {
  412.     register Int i,j;
  413.     register Char *p,*q,c;
  414.     Char buf[128];
  415.  
  416.     if(puttest())
  417.     {
  418.         j = parm1;
  419.         for(p = command,q = buf;(c = *p++);)
  420.         {
  421.             if(c == '%')
  422.             {
  423.                 p++;
  424.                 if(j > 99)
  425.                 {
  426.                     *q++ = (i = (j / 100)) + '0';
  427.                     j -= 100 * i;
  428.                     *q++ = (i = (j / 10)) + '0';
  429.                     j -= 10 * i;
  430.                     *q++ = j + '0';
  431.                 }
  432.                 else if(j > 9)
  433.                 {
  434.                     *q++ = (i = (j / 10)) + '0';
  435.                     j -= 10 * i;
  436.                     *q++ = j + '0';
  437.                 }
  438.                 else
  439.                     *q++ = j + '0';
  440.                 j = parm2;
  441.             }
  442.             else
  443.                 *q++ = c;
  444.         }
  445.         *q = '\0';
  446.         put(buf);
  447.     }
  448. }
  449.  
  450. /******************************************************************************\
  451. |Routine: vtstart
  452. |Callby: init_term wincom
  453. |Purpose: Sets the terminal to a known state.
  454. |Arguments:
  455. |    none
  456. \******************************************************************************/
  457. void vtstart()
  458. {
  459.     ttystart();
  460.     if(!BRAINDEAD) put(coms[VTSTART]);
  461.     vtstartyet = 1;
  462.     EMU_CURATT = 0;
  463. }
  464.  
  465. /******************************************************************************\
  466. |Routine: move
  467. |Callby: bigger_win cfg command edit fix_scroll help imalloc init_term inquire insert insert_win killer load_key match_paren paint paint_window parse_fnm ref_window remove_win scroll_down scroll_up slip_message trim unselect wincom
  468. |Purpose: Positions the cursor.
  469. |Arguments:
  470. |    row is the line number on the screen. The top line is line 1.
  471. |    col is the column number. The leftmost column is column 1.
  472. \******************************************************************************/
  473. void move(row,col)
  474. Int row,col;
  475. {
  476.     col -= FIRSTCOL - 1;
  477.     row = max(1,min(EMU_NROW,row));
  478.     col = max(1,min(EMU_NCOL,col));
  479.     if(coms[MOVE][0] != '~')
  480.         command_two(coms[MOVE],row,col);
  481.     EMU_CURROW = --row;
  482.     EMU_CURCOL = --col;
  483. }
  484.  
  485. /******************************************************************************\
  486. |Routine: marge
  487. |Callby: bigger_win cfg command edit help init_term insert insert_win killer parse_fnm set_window smaller_win
  488. |Purpose: Sets the scrolling region on the screen.
  489. |Arguments:
  490. |    row1 is the top line of the scrolling region.
  491. |    row2 is the bottom line of the scrolling region.
  492. \******************************************************************************/
  493. void marge(row1,row2)
  494. Int row1,row2;
  495. {
  496.     if(coms[MARGE][0] != '~')
  497.         command_two(coms[MARGE],row1,row2);
  498.     scroll_top = max(1,min(EMU_NROW,row1));
  499.     scroll_bot = max(1,min(EMU_NROW,row2));
  500. }
  501.  
  502. /******************************************************************************\
  503. |Routine: del_line
  504. |Callby: bigger_win init_term insert insert_win killer scroll_up
  505. |Purpose: Deletes lines, causing lines below to scroll up.
  506. |Arguments:
  507. |    lines is the number of lines to delete.
  508. \******************************************************************************/
  509. void del_line(lines)
  510. Int lines;
  511. {
  512.     register Int i,from,to,save;
  513.  
  514.     i = scroll_bot - EMU_CURROW;
  515.     lines = max(1,min(i,lines)); 
  516.     if(coms[DEL_LINE][0] == '~')
  517.     {
  518.         if(coms[MARGE][0] == '~')
  519.         {
  520.             for(i = EMU_CURROW + lines;i < scroll_bot;i++)
  521.             {
  522.                 from = i * EMU_NCOL;
  523.                 to = from - EMU_NCOL * lines;
  524.                 memcpy(EMU_SCREEN + to,EMU_SCREEN + from,EMU_NCOL);
  525.                 memcpy(EMU_ATTRIB + to,EMU_ATTRIB + from,EMU_NCOL);
  526.             }
  527.             for(i = scroll_bot - 1;i >= scroll_bot - lines;i--)
  528.             {
  529.                 memset(EMU_SCREEN + i * EMU_NCOL,' ',EMU_NCOL);
  530.                 memset(EMU_ATTRIB + i * EMU_NCOL,0,EMU_NCOL);
  531.             }
  532.             if(!BRAINDEAD) ref_display();
  533.         }
  534.         else
  535.         {
  536.             save = scroll_top;
  537.             marge(EMU_CURROW + 1,scroll_bot);
  538.             move(scroll_bot,1);
  539.             for(i = 0;i < lines;i++)
  540.                 down();
  541.             marge((scroll_top = save),scroll_bot);
  542.         }
  543.     }
  544.     else
  545.     {
  546.         if(coms[MARGE][0] == '~')
  547.         {
  548.             lines = max(1,min(lines,scroll_bot - EMU_CURROW));
  549.             command_one(coms[DEL_LINE],lines);
  550.             if(scroll_bot != EMU_NROW)
  551.             {
  552.                 Int saverow,savecol;
  553.                 
  554.                 saverow = EMU_CURROW + 1;
  555.                 savecol = EMU_CURCOL + 1;
  556.                 move(scroll_bot - lines + 1,1);
  557.                 if(coms[INS_LINE][0] != '~')
  558.                     command_one(coms[INS_LINE],lines);
  559.                 move(saverow,savecol);
  560.             }
  561.         }
  562.         else
  563.             command_one(coms[DEL_LINE],lines);
  564.         for(i = EMU_CURROW + lines;i < scroll_bot;i++)
  565.         {
  566.             from = i * EMU_NCOL;
  567.             to = from - EMU_NCOL * lines;
  568.             memcpy(EMU_SCREEN + to,EMU_SCREEN + from,EMU_NCOL);
  569.             memcpy(EMU_ATTRIB + to,EMU_ATTRIB + from,EMU_NCOL);
  570.         }
  571.         for(i = scroll_bot - 1;i >= scroll_bot - lines;i--)
  572.         {
  573.             memset(EMU_SCREEN + i * EMU_NCOL,' ',EMU_NCOL);
  574.             memset(EMU_ATTRIB + i * EMU_NCOL,0,EMU_NCOL);
  575.         }
  576.     }
  577. }
  578.  
  579. /******************************************************************************\
  580. |Routine: ins_line
  581. |Callby: bigger_win init_term insert killer scroll_down
  582. |Purpose: Inserts lines at the cursor, causing lines below to scroll down.
  583. |Arguments:
  584. |    lines is the number of lines to insert.
  585. \******************************************************************************/
  586. void ins_line(lines)
  587. Int lines;
  588. {
  589.     register Int i,from,to,save;
  590.  
  591.     i = scroll_bot - EMU_CURROW;
  592.     lines = max(1,min(i,lines)); 
  593.     if(coms[INS_LINE][0] == '~')
  594.     {
  595.         if(coms[MARGE][0] == '~')
  596.         {
  597.             for(i = scroll_bot - 1 - lines;i >= EMU_CURROW;i--)
  598.             {
  599.                 from = i * EMU_NCOL;
  600.                 to = from + EMU_NCOL * lines;
  601.                 memcpy(EMU_SCREEN + to,EMU_SCREEN + from,EMU_NCOL);
  602.                 memcpy(EMU_ATTRIB + to,EMU_ATTRIB + from,EMU_NCOL);
  603.             }
  604.             for(i = EMU_CURROW;i < EMU_CURROW + lines;i++)
  605.             {
  606.                 memset(EMU_SCREEN + i * EMU_NCOL,' ',EMU_NCOL);
  607.                 memset(EMU_ATTRIB + i * EMU_NCOL,0,EMU_NCOL);
  608.             }
  609.             if(!BRAINDEAD) ref_display();
  610.         }
  611.         else
  612.         {
  613.             save = scroll_top;
  614.             marge(EMU_CURROW + 1,scroll_bot);
  615.             move(scroll_top,1);
  616.             for(i = 0;i < lines;i++)
  617.                 up();
  618.             marge((scroll_top = save),scroll_bot);
  619.         }
  620.     }
  621.     else
  622.     {
  623.         if(coms[MARGE][0] == '~')
  624.         {
  625.             lines = max(1,min(lines,scroll_bot - EMU_CURROW));
  626.             if(scroll_bot < EMU_NROW)
  627.             {
  628.                 Int saverow,savecol;
  629.                 
  630.                 saverow = EMU_CURROW + 1;
  631.                 savecol = EMU_CURCOL + 1;
  632.                 move(scroll_bot - lines + 1,1);
  633.                 if(coms[DEL_LINE][0] != '~')
  634.                     command_one(coms[DEL_LINE],lines);
  635.                 move(saverow,savecol);
  636.             }
  637.         }
  638.         command_one(coms[INS_LINE],lines);
  639.         for(i = scroll_bot - 1 - lines;i >= EMU_CURROW;i--)
  640.         {
  641.             from = i * EMU_NCOL;
  642.             to = from + EMU_NCOL * lines;
  643.             memcpy(EMU_SCREEN + to,EMU_SCREEN + from,EMU_NCOL);
  644.             memcpy(EMU_ATTRIB + to,EMU_ATTRIB + from,EMU_NCOL);
  645.         }
  646.         for(i = EMU_CURROW;i < EMU_CURROW + lines;i++)
  647.         {
  648.             memset(EMU_SCREEN + i * EMU_NCOL,' ',EMU_NCOL);
  649.             memset(EMU_ATTRIB + i * EMU_NCOL,0,EMU_NCOL);
  650.         }
  651.     }
  652. }
  653.  
  654. /******************************************************************************\
  655. |Routine: up
  656. |Callby: init_term
  657. |Purpose: Moves the cursor up one line. Scrolls down if the cursor is at the
  658. |         top of the scrolling region.
  659. |Arguments:
  660. |    none
  661. \******************************************************************************/
  662. void up()
  663. {
  664.     if(coms[MARGE][0] == '~' && EMU_CURROW == scroll_top - 1)
  665.     {
  666.         ins_line(1);
  667.         return;
  668.     }
  669.     if(coms[UP][0] != '~')
  670.         put(coms[UP]);
  671.     if(EMU_CURROW == scroll_top - 1)
  672.     {
  673.         Int i,from,to;
  674.         
  675.         for(i = scroll_bot - 2;i >= EMU_CURROW;i--)
  676.         {
  677.             from = i * EMU_NCOL;
  678.             to = from + EMU_NCOL;
  679.             memcpy(EMU_SCREEN + to,EMU_SCREEN + from,EMU_NCOL);
  680.             memcpy(EMU_ATTRIB + to,EMU_ATTRIB + from,EMU_NCOL);
  681.         }
  682.         memset(EMU_SCREEN + EMU_CURROW * EMU_NCOL,' ',EMU_NCOL);
  683.         memset(EMU_ATTRIB + EMU_CURROW * EMU_NCOL,0,EMU_NCOL);
  684.     }
  685.     else
  686.     {
  687.         if(--EMU_CURROW < 0)
  688.             EMU_CURROW = 0;
  689.     }
  690. }
  691.  
  692. /******************************************************************************\
  693. |Routine: down
  694. |Callby: init_term
  695. |Purpose: Moves the cursor down one line. Scrolls up if the cursor is at the
  696. |         bottom of the scrolling region.
  697. |Arguments:
  698. |    none
  699. \******************************************************************************/
  700. void down()
  701. {
  702.     if(coms[MARGE][0] == '~' && EMU_CURROW == scroll_bot - 1)
  703.     {
  704.         Int savecol;
  705.  
  706.         savecol = EMU_CURCOL + 1;
  707.         move(scroll_top,1);
  708.         del_line(1);
  709.         move(scroll_bot,savecol);
  710.         return;
  711.     }
  712.     if(coms[DOWN][0] != '~')
  713.         put(coms[DOWN]);
  714.     if(EMU_CURROW == scroll_bot - 1)
  715.     {
  716.         Int i,from,to;
  717.         
  718.         for(i = scroll_top - 1;i < scroll_bot - 1;i++)
  719.         {
  720.             to = i * EMU_NCOL;
  721.             from = to + EMU_NCOL;
  722.             memcpy(EMU_SCREEN + to,EMU_SCREEN + from,EMU_NCOL);
  723.             memcpy(EMU_ATTRIB + to,EMU_ATTRIB + from,EMU_NCOL);
  724.         }
  725.         memset(EMU_SCREEN + (scroll_bot - 1) * EMU_NCOL,' ',EMU_NCOL);
  726.         memset(EMU_ATTRIB + (scroll_bot - 1) * EMU_NCOL,0,EMU_NCOL);
  727.     }
  728.     else
  729.     {
  730.         if(++EMU_CURROW >= EMU_NROW)
  731.             EMU_CURROW = EMU_NROW - 1;
  732.     }
  733. }
  734.  
  735. /******************************************************************************\
  736. |Routine: reverse
  737. |Callby: command edit help inquire load_key match_paren ref_window remove_win select slip_message
  738. |Purpose: Turns on reverse video.
  739. |Arguments:
  740. |    none
  741. \******************************************************************************/
  742. void reverse()
  743. {
  744.     if(coms[REVERSE][0] != '~')
  745.         put(coms[REVERSE]);
  746.     EMU_CURATT |= 1;
  747. }
  748.  
  749. /******************************************************************************\
  750. |Routine: normal
  751. |Callby: command edit help inquire load_key match_paren ref_window remove_win select slip_message
  752. |Purpose: Turns off reverse video, bold, underline, flash.
  753. |Arguments:
  754. |    none
  755. \******************************************************************************/
  756. void normal()
  757. {
  758.     if(coms[NORMAL][0] != '~')
  759.         put(coms[NORMAL]);
  760.     EMU_CURATT = 0;
  761. }
  762.  
  763. /******************************************************************************\
  764. |Routine: bell
  765. |Callby: command edit wincom
  766. |Purpose: Beeps the terminal.
  767. |Arguments:
  768. |    none
  769. \******************************************************************************/
  770. void bell()
  771. {
  772.     if(coms[BELL][0] != '~')
  773.         put(coms[BELL]);
  774.     else
  775.         ring_bell();
  776. }
  777.  
  778. /******************************************************************************\
  779. |Routine: ers_screen
  780. |Callby: cfg ref_display
  781. |Purpose: Erases the entire screen.
  782. |Arguments:
  783. |    none
  784. \******************************************************************************/
  785. void ers_screen()
  786. {
  787.     if(coms[ERS_SCREEN][0] != '~')
  788.         put(coms[ERS_SCREEN]);
  789.     memset(EMU_SCREEN,' ',EMU_NCOL * EMU_NROW);
  790.     memset(EMU_ATTRIB,0,EMU_NCOL * EMU_NROW);
  791. }
  792.  
  793. /******************************************************************************\
  794. |Routine: ers_end
  795. |Callby: edit help init_term inquire load_key paint paint_window parse_fnm ref_window remove_win slip_message
  796. |Purpose: Erases everything to the right of the cursor on the current line.
  797. |Arguments:
  798. |    none
  799. \******************************************************************************/
  800. void ers_end()
  801. {
  802.     register Int off;
  803.     register Char *s,*a,*end;
  804.  
  805.     off = EMU_CURROW * EMU_NCOL + EMU_CURCOL;
  806.     s = EMU_SCREEN + off;
  807.     a = EMU_ATTRIB + off;
  808.     end = s + EMU_NCOL - EMU_CURCOL;
  809.     while(s != end)
  810.     {
  811.         if(*s != ' ' || *a != 0)
  812.             break;
  813.         s++;
  814.         a++;
  815.     }
  816.     if(s != end)
  817.     {
  818.         if(coms[ERS_END][0] != '~')
  819.             put(coms[ERS_END]);
  820.         memset(EMU_SCREEN + off,' ',EMU_NCOL - EMU_CURCOL);
  821.         memset(EMU_ATTRIB + off,0,EMU_NCOL - EMU_CURCOL);
  822.     }
  823. }
  824.  
  825. /******************************************************************************\
  826. |Routine: right
  827. |Callby: paint paint_window put
  828. |Purpose: Moves the cursor to the right.
  829. |Arguments:
  830. |    cols is the number of columns to move.
  831. \******************************************************************************/
  832. void right(cols)
  833. Int cols;
  834. {
  835.     if((cols = min(EMU_NCOL - EMU_CURCOL - 1,cols)))
  836.     {
  837.         if(coms[RIGHT][0] != '~')
  838.             command_one(coms[RIGHT],cols);
  839.         EMU_CURCOL += cols;
  840.     }
  841. }
  842.  
  843. /******************************************************************************\
  844. |Routine: left
  845. |Callby: not called
  846. |Purpose: Moves the cursor to the left.
  847. |Arguments:
  848. |    cols is the number of columns to move.
  849. \******************************************************************************/
  850. void left(cols)
  851. Int cols;
  852. {
  853.     if((cols = min(EMU_CURCOL,cols)))
  854.     {
  855.         if(coms[LEFT][0] != '~')
  856.             command_one(coms[LEFT],cols);
  857.         EMU_CURCOL -= cols;
  858.     }
  859. }
  860.  
  861. /******************************************************************************\
  862. |Routine: next
  863. |Callby: cfg help paint paint_window parse_fnm
  864. |Purpose: Moves the cursor to the beginning of the next line on the screeen.
  865. |         Scrolls up if the cursor is at the bottom of the scrolling region.
  866. |Arguments:
  867. |    none
  868. \******************************************************************************/
  869. void next()
  870. {
  871.     if(coms[NEXT][0] != '~')
  872.         put(coms[NEXT]);
  873.     EMU_CURCOL = 0;
  874.     if(EMU_CURROW == scroll_bot - 1)
  875.     {
  876.         Int i,from,to;
  877.         
  878.         for(i = scroll_top - 1;i < scroll_bot - 1;i++)
  879.         {
  880.             to = i * EMU_NCOL;
  881.             from = to + EMU_NCOL;
  882.             memcpy(EMU_SCREEN + to,EMU_SCREEN + from,EMU_NCOL);
  883.             memcpy(EMU_ATTRIB + to,EMU_ATTRIB + from,EMU_NCOL);
  884.         }
  885.         memset(EMU_SCREEN + (scroll_bot - 1) * EMU_NCOL,' ',EMU_NCOL);
  886.         memset(EMU_ATTRIB + (scroll_bot - 1) * EMU_NCOL,0,EMU_NCOL);
  887.     }
  888.     else
  889.     {
  890.         if(++EMU_CURROW >= EMU_NROW)
  891.             EMU_CURROW = EMU_NROW - 1;
  892.     }
  893. }
  894.  
  895. /******************************************************************************\
  896. |Routine: eob
  897. |Callby: insert killer paint paint_window unselect
  898. |Purpose: Displays the end-of-buffer string (default is [eob]).
  899. |Arguments:
  900. |    none
  901. \******************************************************************************/
  902. void eob()
  903. {
  904.     putz(coms[EOB]);
  905. }
  906.  
  907. /******************************************************************************\
  908. |Routine: cr
  909. |Callby: cfg help inquire paint paint_window unselect
  910. |Purpose: Moves the cursor to the beginning of the current line.
  911. |Arguments:
  912. |    none
  913. \******************************************************************************/
  914. void cr()
  915. {
  916.     if(coms[CR][0] != '~')
  917.         put(coms[CR]);
  918.     EMU_CURCOL = 0;
  919. }
  920.  
  921. /******************************************************************************\
  922. |Routine: ers_bottom
  923. |Callby: insert_win wincom
  924. |Purpose: Erases from the cursor to the bottom of the screen.
  925. |Arguments:
  926. |    none
  927. \******************************************************************************/
  928. void ers_bottom()
  929. {
  930.     register Int off,ndel;
  931.     
  932.     if(coms[ERS_BOTTOM][0] != '~')
  933.         put(coms[ERS_BOTTOM]);
  934.     off = EMU_CURROW * EMU_NCOL + EMU_CURCOL;
  935.     ndel = EMU_NROW * EMU_NCOL - off;
  936.     memset(EMU_SCREEN + off,' ',ndel);
  937.     memset(EMU_ATTRIB + off,0,ndel);
  938. }
  939.  
  940. /******************************************************************************\
  941. |Routine: vtend
  942. |Callby: parse_fnm restore_par wincom
  943. |Purpose: Resets the terminal when ED terminates.
  944. |Arguments:
  945. |    none
  946. \******************************************************************************/
  947. void vtend()
  948. {
  949.     if(vtstartyet && !BRAINDEAD) put(coms[VTEND]);
  950.     if(BRAINDEAD)
  951.     {
  952.         EMU_CURCOL = 0;
  953.         ers_end();
  954.         if(--EMU_CURROW < 0) EMU_CURROW = 0;
  955.     }
  956.     putout();
  957.     ttyend();
  958.     EMU_CURATT = 0;
  959. }
  960.  
  961. /******************************************************************************\
  962. |Routine: unmatchable
  963. |Callby: wincom
  964. |Purpose: Renders an area on the screen 'unmatchable' to insure new data bound
  965. |         there is actually displayed.
  966. |Arguments:
  967. |    top is the top line of the area to be made unmatchable.
  968. |    bot is the bottom line of the area.
  969. \******************************************************************************/
  970. void unmatchable(top,bot)
  971. Int top,bot;
  972. {
  973.     memset(EMU_ATTRIB + (top - 1) * EMU_NCOL,-1,(bot - top + 1) * EMU_NCOL);
  974. }
  975.  
  976. /******************************************************************************\
  977. |Routine: get_next_key
  978. |Callby: edit help inquire load_key slip_message
  979. |Purpose: Gets the next character in the input stream (either from the terminal
  980. |         or the journal file). Returns the repeat count to be applied to the
  981. |         key.
  982. |Arguments:
  983. |    buf is the return key value.
  984. \******************************************************************************/
  985. Int get_next_key(buf)
  986. Schar *buf;
  987. {
  988.     register Int b,i;
  989.     Int repeat;
  990.  
  991. /* if emulated keys await, do that */
  992.     if(emulptr < emulend)
  993.     {
  994.         *buf = *emulptr++;
  995.         return(*reptptr++);
  996.     }
  997. /* if a defined key is evolving, get from there */
  998.     if(evolving)
  999.     {
  1000.         if((evolving = next_key(&chrbuf,&repeat)))
  1001.         {
  1002.             *buf = chrbuf;
  1003.             return(repeat);
  1004.         }
  1005.         if(was_putoff() && !processmode)
  1006.         {
  1007.             puton();
  1008.             ref_display();
  1009.             move(CURROW,CURCOL);
  1010.         }
  1011.     }
  1012. /* if recovery is in progress, get from there */
  1013.     if(recovering)
  1014.     {
  1015.         unjournal(buf,&repeat);
  1016.         if(repeat > 0)
  1017.         {
  1018.             if((evolving = defined_key(*buf,repeat)))
  1019.             {
  1020.                 next_key(&chrbuf,&repeat);
  1021.                 *buf = chrbuf;
  1022.             }
  1023.             return(repeat);
  1024.         }
  1025.         if(processmode)
  1026.         {
  1027.             printf("That journal file terminates abnormally.\r\n");
  1028.             cleanup(-1);
  1029.         }
  1030.         recovering = 0;
  1031.     }
  1032. /* dump the output buffer and prepare */
  1033.     putout();
  1034.     repeat = 1;
  1035. /* report_history contains input bytes that only partially matched a known sequence */
  1036.     if(report_history)
  1037.     {
  1038.         if(report_history == next_history)    /* we hit the end of the history buffer, wait for more input */
  1039.         {
  1040.             next_history = history;
  1041.             report_history = NULL;
  1042.             cur = &base;
  1043.         }
  1044.         else
  1045.         {
  1046.             *buf = *report_history++;
  1047.             journal(*buf,1);
  1048.             if((evolving = defined_key(*buf,1)))    /* report from history buffer */
  1049.             {
  1050.                 next_key(&chrbuf,&repeat);
  1051.                 *buf = chrbuf;
  1052.             }
  1053.             return(repeat);
  1054.         }
  1055.     }
  1056. /* no other form of input, get from the terminal */
  1057.     while(1)
  1058.     {
  1059.         ttyget(&chrbuf);    /* get byte from user */
  1060. traverse:
  1061.         for(b = 0;b < cur->branches;b++)    /* see if it is known at current node */
  1062.             if(cur->chars[b] == chrbuf)
  1063.                 break;
  1064.         if(b < cur->branches)    /* it exists at this node */
  1065.         {
  1066.             *next_history++ = chrbuf;    /* store in history buffer, in case we get a miss later */
  1067.             cur = cur->nodes[b];    /* advance to subnode for that byte */
  1068.             if(cur->leaf && !cur->branches)    /* it is a leaf, report it */
  1069.             {
  1070.                 *buf = cur->value;    /* return value */
  1071.                 next_history = history;    /* clear the history buffer */
  1072.                 cur = &base;    /* initialize the tree search */
  1073.                 journal(*buf,repeat);
  1074.                 if((evolving = defined_key(*buf,repeat)))
  1075.                 {
  1076.                     next_key(&chrbuf,&repeat);
  1077.                     *buf = chrbuf;
  1078.                 }
  1079.                 return(repeat);
  1080.             }
  1081.         }
  1082.         else    /* a miss */
  1083.         {
  1084.             if(cur == &base)    /* special, simple, handling for top-level miss (a common occurrence) */
  1085.             {
  1086.                 if((chrbuf & 0x80) && chrbuf >= KEY_SCROLLLEFT)    /* last in edit */
  1087.                 {
  1088.                     *buf = KEY_SPECIALINSERT;    /* NCS support for 8 bit chars */
  1089.                     repeat = chrbuf;
  1090.                 }
  1091.                 else
  1092.                     *buf = chrbuf;
  1093.                 journal(*buf,repeat);
  1094.                 if((evolving = defined_key(*buf,repeat)))
  1095.                 {
  1096.                     next_key(&chrbuf,&repeat);
  1097.                     *buf = chrbuf;
  1098.                     return(repeat);
  1099.                 }
  1100.             }
  1101.             else
  1102.             {
  1103.                 if(cur->leaf)    /* previous sequence was allowed to (optionally) terminate, stick its value on stack */
  1104.                 {
  1105.                     if(cur->value == KEY_GOLD)
  1106.                     {
  1107.                         if(isdigit(chrbuf))    /* special handling for GOLD */
  1108.                         {
  1109.                             repeat = 0;
  1110.                             while(isdigit(chrbuf))  /* acquire digits and build up repeat count */
  1111.                             {
  1112.                                 repeat = 10 * repeat + chrbuf - '0';
  1113.                                 ttyget(&chrbuf);
  1114.                             }
  1115.                             cur = &base;
  1116.                             next_history = history;
  1117.                             goto traverse;
  1118.                         }
  1119.                         else    /* flag gold-<key> with negative repeat count */
  1120.                         {
  1121.                             i = toupper(chrbuf);
  1122.                             if(i >= 'A' && i <= 'Z')
  1123.                                 chrbuf = i + 0x80 - 'A';
  1124.                             cur = &base;
  1125.                             next_history = history;
  1126.                             goto traverse;
  1127.                         }
  1128.                     }
  1129.                     history[0] = cur->value;
  1130.                     next_history = history+1;
  1131.                 }
  1132.                 *next_history++ = chrbuf;
  1133.                 report_history = history + 1;
  1134.                 *buf = history[0];
  1135.                 journal(*buf,repeat);
  1136.                 if((evolving = defined_key(*buf,repeat)))
  1137.                 {
  1138.                     next_key(&chrbuf,&repeat);
  1139.                     *buf = chrbuf;
  1140.                 }
  1141.             }
  1142.             return(repeat);
  1143.         }
  1144.     }
  1145. }
  1146.  
  1147. /******************************************************************************\
  1148. |Routine: set_recover
  1149. |Callby: journal
  1150. |Purpose: Sets a flag that tells get_next_key to get characters from the
  1151. |         journal file instead of the terminal.
  1152. |Arguments:
  1153. |    none
  1154. \******************************************************************************/
  1155. void set_recover()
  1156. {
  1157.     recovering = 1;
  1158. }
  1159.  
  1160. /******************************************************************************\
  1161. |Routine: get_eob_char
  1162. |Callby: select
  1163. |Purpose: Returns the first character of the user's <eob> string.
  1164. |Arguments:
  1165. |    none
  1166. \******************************************************************************/
  1167. Int get_eob_char()
  1168. {
  1169.     register Int i;
  1170.     
  1171.     if(!(i = coms[EOB][0]))
  1172.         i = ' ';
  1173.     return(i);
  1174. }
  1175.  
  1176.